﻿/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.XPath;
using DaveSexton.DocProject.Sandcastle.ContentManagement;
using DaveSexton.Controls;
using System.Runtime.InteropServices;

namespace DaveSexton.DocProject.Sandcastle.TopicManagement
{
	internal sealed class TopicPropertiesManager
	{
		#region Public Properties
		public bool IsDirty { get; internal set; }

		public TopicMetadata this[Topic topic]
		{
			get
			{
				if (topic == null)
					throw new ArgumentNullException("topic");
				
				if (topic.TopicType != TopicType.Conceptual)
					throw new ArgumentException(string.Format(System.Globalization.CultureInfo.CurrentCulture, 
						Resources.Errors.ConceptualTopicRequired, topic.Id));

				return this[topic.File, topic.Id];
			}
		}

		public TopicMetadata this[string topicFile, string id]
		{
			get
			{
				if (string.IsNullOrEmpty(topicFile))
					throw new ArgumentException(Resources.Errors.NullOrEmpty, "topicFile");

				if (string.IsNullOrEmpty(id))
					throw new ArgumentException(Resources.Errors.NullOrEmpty, "id");

				TopicMetadata value;

				if (metadata.Contains(id))
					value = metadata[id];
				else
				{
					if (string.IsNullOrEmpty(topicFile))
						throw new InvalidOperationException(Resources.Errors.TopicFileUnknown);

					string metadataFile = GetCompanionXmlFile(topicFile);

					value = TopicMetadataXml.Load(metadataFile, id);

					if (value == null)
					{
						value = new TopicMetadata(id);
						value.TocTitle = System.IO.Path.GetFileNameWithoutExtension(topicFile);

						metadataFile = null;
						metadata.Add(value);
					}
					else
						metadata.Add(value);

					value.PropertyChanged += (s, e) => OnPropertyChanged(e);

					topicData[id] = new[] { metadataFile, topicFile };

					value.PropertyChanged += (s, e) => IsDirty = true;
				}

				return value;
			}
		}
		#endregion

		#region Private / Protected
		private readonly IDocProject project;
		private readonly string topicsPath;
		private readonly TopicMetadataCollection metadata = new TopicMetadataCollection();
		private readonly Dictionary<string, string[]> topicData = new Dictionary<string, string[]>();
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="TopicPropertiesManager" /> class.
		/// </summary>
		public TopicPropertiesManager(IDocProject project, string topicsPath)
		{
			if (project == null)
				throw new ArgumentNullException("project");

			this.project = project;
			this.topicsPath = topicsPath;
		}
		#endregion

		#region Methods
		private string GetCompanionXmlFile(string topicFile)
		{
			return System.IO.Path.ChangeExtension(System.IO.Path.Combine(topicsPath, topicFile), 
				ConceptualTopicFileManager.TopicFileCompanionExtension);
		}

		public void SaveChanges()
		{
			if (IsDirty)
			{
				foreach (TopicMetadata data in metadata)
				{
					if (data.IsDirty)
					{
						string[] info = topicData[data.TopicId];
						string metadataFile = info[0];
						string topicFile = info[1];

						if (string.IsNullOrEmpty(metadataFile))
							metadataFile = GetCompanionXmlFile(topicFile);

						string file = System.IO.Path.Combine(topicsPath, topicFile);

						IProjectItem folder = project.GetFolder(topicsPath);
						folder.Show();

						IProjectItem item = project.GetItem(metadataFile);

						item.Checkout();

						TopicMetadataXml writer = new TopicMetadataXml(metadataFile);
						writer.WriteCompanionFile(data.TopicId, System.IO.Path.GetFileNameWithoutExtension(topicFile), data);

						data.IsDirty = false;

						item.Show();

						IProjectItem topicItem = project.GetItem(file);

						try
						{
							if (topicItem.Exists)
								topicItem.MakeDependencyFor(metadataFile);
						}
						catch (COMException) { }		// occurred in testing when the topicItem file did not exist
					}
				}

				// NOTE: Force metadata to be reloaded from the TOC file when it's needed by clearing the collection now; 
				// otherwise, manually renaming the topic files on disc won't be supported since the data in memory will be 
				// unable to refresh.
				metadata.Clear();
				topicData.Clear();

				IsDirty = false;
			}
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
		public void CommitChanges()
		{
			// do nothing
		}

		public void CancelChanges()
		{
			metadata.Clear();
			topicData.Clear();

			IsDirty = false;
		}
		#endregion

		#region Events
		private readonly object PropertyChangedEventLock = new object();
		private EventHandler PropertyChangedEvent;

		/// <summary>
		/// Evenr raised when a property changes for a topic.
		/// </summary>
		public event EventHandler PropertyChanged
		{
			add
			{
				lock (PropertyChangedEventLock)
				{
					PropertyChangedEvent += value;
				}
			}
			remove
			{
				lock (PropertyChangedEventLock)
				{
					PropertyChangedEvent -= value;
				}
			}
		}

		/// <summary>
		/// Raises the <see cref="PropertyChanged" /> event.
		/// </summary>
		/// <param name="e"><see cref="EventArgs" /> object that provides the arguments for the event.</param>
		private void OnPropertyChanged(EventArgs e)
		{
			EventHandler handler = null;

			lock (PropertyChangedEventLock)
			{
				handler = PropertyChangedEvent;
			}

			if (handler != null)
				handler(this, e);
		}
		#endregion
	}
}
